package tcp_server

import (
	"ServerDemo/config"
	"ServerDemo/protocal/protobuf"
	"fmt"
	"google.golang.org/protobuf/proto"
	"log"
	"time"

	"github.com/panjf2000/ants"
	"github.com/panjf2000/gnet"
)

type HandlerDataType struct {
	ProtocalData *protobuf.ProtocalData
	Data         interface{}
	Conn         gnet.Conn

	server *TCPServer
}
type ServerHandler func(handlerData *HandlerDataType)

var defaultHandler ServerHandler = func(handlerData *HandlerDataType) {
	if handlerData.ProtocalData == nil {
		return
	}
	fmt.Println("服务端收到数据：", handlerData.Data)

	//process
	switch handlerData.ProtocalData.ActionType {
	case config.ACTION_PING:
		fmt.Println("-----------------------服务端处理PING请求------------------------------")
	}
}

func NewTCPServer(port int) *TCPServer {

	options := ants.Options{ExpiryDuration: time.Second * 10, Nonblocking: true}
	defaultAntsPool, _ := ants.NewPool(config.DefaultAntsPoolSize, ants.WithOptions(options))

	server := &TCPServer{}

	server.Port = port
	server.WorkerPool = defaultAntsPool
	server.Handler = defaultHandler

	return server
}

type TCPServer struct {
	*gnet.EventServer
	Port       int
	WorkerPool *ants.Pool

	ConnNum int

	Handler ServerHandler
}

func (svr *TCPServer) OnInitComplete(srv gnet.Server) (action gnet.Action) {
	log.Printf("Echo server is listening on %s (multi-cores: %t, loops: %d)\n",
		srv.Addr.String(), srv.Multicore, srv.NumEventLoop)
	return
}

func (svr *TCPServer) OnOpened(c gnet.Conn) (out []byte, action gnet.Action) {
	svr.ConnNum = svr.ConnNum + 1
	return
}

func (svr *TCPServer) OnClosed(c gnet.Conn, err error) (action gnet.Action) {
	svr.ConnNum = svr.ConnNum - 1
	return
}

func (svr *TCPServer) React(frame []byte, c gnet.Conn) (out []byte, action gnet.Action) {

	//在 reactor 协程中做解码操作
	protocal := protobuf.NewProtobufProtocal()
	protocal.SetGnetConnection(c)

	protocalData, err := protocal.ServerDecode(frame)
	if err != nil {
		log.Printf("React WorkerPool Decode error :%v\n", err)
	}

	data := &protobuf.TestRequest{}
	err = proto.Unmarshal(protocalData.Data, data)
	if err != nil {
		return
	}
	out, _ = protocal.Encode(config.ACTION_PONG, data)

	err = svr.WorkerPool.Submit(func() {
		//具体业务在 worker pool中处理
		handlerData := &HandlerDataType{}
		handlerData.ProtocalData = protocalData
		handlerData.Data = data
		handlerData.Conn = c
		handlerData.server = svr
		svr.Handler(handlerData)
	})

	if err != nil {
		return out, 0
	}
	return
}
